/****************************************************************************
*
* Copyright (c) 2023  C*Core -   All Rights Reserved
*
* THIS SOFTWARE IS DISTRIBUTED "AS IS, " AND ALL WARRANTIES ARE DISCLAIMED,
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* PROJECT     : CCFC2011BC
* DESCRIPTION : CCFC2011BC rtc low level drivers code
* HISTORY     : Initial version
* @file     rtc_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/
#include "rtc_lld.h"
#include "lldconf.h"
#include "console.h"
#include "IntcInterrupts.h"
/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */
/* Driver exported variables.                                                */
/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */

RTCAPIDriver RTCAPID1;

/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */
/* Driver local variables and types.                                         */
/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */

/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */
/* Driver interrupt handlers.                                                */
/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */

static void RTC_Interrupt_Handler(RTCAPIDriver *RtcApip)
{
    //  CTEN When negated asynchronously resets the counter and synchronously enables the counter when enable
    RtcApip->rtcapi->RTCC.B.CNTEN = 0;
    RtcApip->rtcapi->RTCS.B.RTCF = 1;
    RtcApip->rtcapi->RTCC.B.CNTEN = 1;

    /* call the callback, if set */
    if (RtcApip->rtcapi_config->rtccallback != NULL)
    {
      RtcApip->rtcapi_config->rtccallback(RtcApip);
    }
}

static void RTC_API_Interrupt_Handler(RTCAPIDriver *RtcApip)
{
    /* Acknowledge the interrupt, no bit field access on TIF */
    RtcApip->rtcapi->RTCS.B.APIF = 1;

    /* call the callback, if set */
    if (RtcApip->rtcapi_config->apicallback != NULL)
    {
        RtcApip->rtcapi_config->apicallback(RtcApip);
    }
}

/**
 * @brief   rtc interrupt handler.
 *
 * @isr
 */
static IRQ_HANDLER(RTC_HANDLER) /* PRQA S 0625, 1505 */
{
    IRQ_PROLOGUE();

    RTC_Interrupt_Handler(&RTCAPID1);

    IRQ_EPILOGUE();
}

/**
 * @brief   rtc api interrupt handler.
 *
 * @isr
 */
static IRQ_HANDLER(API_HANDLER) /* PRQA S 1505 */
{
    IRQ_PROLOGUE();

    RTC_API_Interrupt_Handler(&RTCAPID1);

    IRQ_EPILOGUE();
}

/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */
/* Driver exported functions.                                                */
/* ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==   = */

/*******************************************************************************
 * @brief      configure the  RTCVAL or APIVAL of RTC/API peripheral  according 
 *             to user configuration
 * @param[in]  RtcApip      pointer to the @p RTCAPIDriver object
 *             msec
 * @param[out] period
 * @retval     None
 * @notapi 
 *******************************************************************************/
uint8_t RTCAPI_LLD_Set_Period_Msec(const RTCAPIDriver *RtcApip, uint32_t msec, uint32_t *period) /* PRQA S 1505 */
{
    /*default clock on reset is SIRC divided by 4, so  CLKSEL_SIRC equals to CLKSEL_SXOSC is 32KHZ*/
    if ((RtcApip->rtcapi->RTCC.B.CLKSEL == RTCC_CLKSEL_SXOSC) || (RtcApip->rtcapi->RTCC.B.CLKSEL == RTCC_CLKSEL_SIRC))
    {
        if (RtcApip->rtcapi_config->rtcenabled == 1U)
        {
            if (RtcApip->rtcapi->RTCC.B.DIV32EN == 1U)
            {
                if (msec >= 1000U)
                {
                    *period = msec/1000U;  //  1/(32K/32)*10^3 *10^3
                }
                else
                {
                    PSPRINTF("\n RTCC_CLKSEL_SIRCDIV , DIV32EN = 1, minimum vaule is 1024(ms)!\n");
                    return 0;
                }
            }
            else
            {
                if (msec >= 32U)
                {
                    *period = msec*125U/4U;  /* 1/(32K)*10^3  */
                }
                else
                {
                    PSPRINTF("\n RTCC_CLKSEL_SIRCDIV , DIV32EN = 0, minimum vaule is 32(ms)!\n");
                    return 0;
                }
            }
        }
        if (RtcApip->rtcapi_config->apienabled == 1U)
        {
        if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 1U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 1U))
            {
                if (msec >= 512U)
                {
                  *period = msec /512U;  //  1/(32K/(512*32))
                }
                else
                {
                    PSPRINTF("\n RTCC_CLKSEL_FIRCDIV , DIV32EN = 1, DIV512EN = 1, minimum vaule is 0.512(ms)!\n");
                    return 0;
                }
            }
            else if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 0U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 1U))
            {
                if (msec >= 16U)
                {
                    *period = msec/16U;  //  1/(32K/512)
                }
                else
                {
                    PSPRINTF("\n RTCC_CLKSEL_FIRCDIV , DIV32EN = 0, DIV512EN = 1, minimum vaule is 16(ms)!\n");
                    return 0;
                }
            }
            else if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 1U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 0U))
            {
                *period = msec;  //  1/(32K/32)
            }
            else
            {
                *period = msec*32U;  //  1/(32K)
            }
        }
    }
    else
    {
        if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 1U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 1U))
        {
            if (msec >= 1024U)
            {
              *period = msec / 1024U ;  //  1/(16M)* 10^3
            }
            else
            {
                PSPRINTF("\n RTCC_CLKSEL_FIRCDIV , DIV32EN = 1, DIV512EN = 1, minimum vaule is 1024(ms)!\n");
                return 0;
            }
        }
        else if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 0U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 1U))
        {
            if (msec >= 32U)
            {
                *period = msec / 32U;  //  1/(16M/512)* 10^3
            }
            else
            {
                PSPRINTF("\n RTCC_CLKSEL_FIRCDIV , DIV32EN = 0, DIV512EN = 1, minimum vaule is 32(ms)!\n");
                return 0;
            }
        }
        else if ((RtcApip->rtcapi->RTCC.B.DIV32EN == 1U) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 0U))
        {
            if (msec >= 2U)
            {
                *period = msec / 2U;  //  1/(16M/32)* 10^3
            }
            else
            {
                PSPRINTF("\n RTCC_CLKSEL_FIRCDIV , DIV32EN = 1, DIV512EN = 0, minimum vaule is 2(ms)!\n");
                return 0;
            }
        }
        else
        {
            *period =  msec *16U;  //  1/(16M)* 10^3
        }
    }
    return 1;
}

/*******************************************************************************
 * @brief      initialize the RTC/API peripheral
 * @param[in]  rtcapip      pointer to the @p RTCAPIDriver object
 *             config       pointer to the @p config. 
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void RTCAPI_LLD_Init(RTCAPIDriver *RtcApip,  RTCAPI_Config *config)
{
    uint32_t ret = 0;
    uint32_t api_period = 0, rtc_period = 0;
     /* set driver configuration */
     RtcApip->rtcapi_config = config;

#if (RTCAPI_USE == TRUE)
    RTCAPID1.rtcapi = &RTC;

  /*Otherwise all other registers are accessible in supervisor mode only */
    RTCAPID1.rtcapi->RTCSUPV.B.SUPV = 0;

    RTCAPID1.rtcapi->RTCC.R = 0x00;
#if (RTCAPI_FREEZE_IN_DEBUG_MODE == TRUE)
    RTCAPID1.rtcapi->RTCC.B.FRZEN = 1;
#endif

    if (config->clocksource == RTCC_CLKSEL_SXOSC)
    {
        SIU.PCR[24].R = 0x0000;  //  PB8
        SIU.PCR[25].R = 0x0000;  //  PB9
        CGM.SXOSC_CTL.B.OSCON = 1;
        while (CGM.SXOSC_CTL.B.OSCON == 0U)
        {
            ;
        }
    }
  /* running frequency */
    RTCAPID1.rtcapi->RTCC.B.CLKSEL = config->clocksource;
    RTCAPID1.rtcapi->RTCC.B.DIV512EN = 0;
    RTCAPID1.rtcapi->RTCC.B.DIV32EN = 1;
    if ((RtcApip->rtcapi_config->rtcenabled == TRUE) &&
        (config->clocksource != RTCC_CLKSEL_FIRC) && (RtcApip->rtcapi->RTCC.B.DIV512EN == 1U))
    {
        PSPRINTF("\n Warning:The RTC provides a configurable divider by 512 to be optionally used by When FIRC is Selected!\n");
    }
#endif /* SWT_USE_SWT0 */

    if (RtcApip->rtcapi_config->rtcenabled == TRUE)
    {
        ret = RTCAPI_LLD_Set_Period_Msec(RtcApip, RtcApip->rtcapi_config->rtc_timeout, &rtc_period);
        if (ret == 0U)
        {
            PSPRINTF("\n rtc_timeout_period set failed!\n");
        }
        if (rtc_period >= 4096U)
        {
            PSPRINTF("\n rtc_timeout_period overflow!\n");
        }
        RtcApip->rtcapi->RTCC.B.RTCVAL = rtc_period & 0xfffU;
        RtcApip->rtcapi->RTCS.B.RTCF = 1;
        (void)INTC_InstallINTCInterruptHandler(RTC_HANDLER, RTC_INT_NUMBER, RTC_IRQ_PRIO);
        RtcApip->rtcapi->RTCC.B.RTCIE = 1;
    }

    if (RtcApip->rtcapi_config->apienabled == TRUE)
    {
        ret = RTCAPI_LLD_Set_Period_Msec(RtcApip, RtcApip->rtcapi_config->api_timeout, &api_period);
        if (ret == 0U)
        {
             PSPRINTF("\n api_timeout_period set failed!\n");
        }
        if (api_period < 4U)
        {
            PSPRINTF("\n The minimum supported value of APIVAL is 4!\n");
        }
        if (api_period >= 1024U)
        {
            PSPRINTF("\n api_timeout_period overflow!\n");
        }
        RtcApip->rtcapi->RTCC.B.APIVAL = api_period & 0x3ffU;

        (void)INTC_InstallINTCInterruptHandler(API_HANDLER, API_INT_NUMBER, API_IRQ_PRIO);
        RtcApip->rtcapi->RTCC.B.APIIE = 1;
        /* RTC enabled. */
        RtcApip->rtcapi->RTCC.B.APIEN = 1;
    }
}

/*******************************************************************************
 * @brief      Changes the period the RTC peripheral.
 * @param[in]  pwmp      pointer to a @p RTCAPIDriver object
 *             period    new cycle time in ticks
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void RTC_LLD_Change_Period(const RTCAPIDriver *RtcApip, uint32_t period)
{
      RtcApip->rtcapi->RTCC.B.CNTEN = 0;
      RtcApip->rtcapi->RTCC.B.RTCVAL = period & 0x0fffU;
      RtcApip->rtcapi->RTCC.B.CNTEN = 1;
}

/*******************************************************************************
 * @brief      Changes the period the API peripheral.
 * @param[in]  pwmp      pointer to a @p RTCAPIDriver object
 *             period    new cycle time in ticks
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void API_LLD_Change_Period(const RTCAPIDriver *RtcApip, uint32_t period)
{
      RtcApip->rtcapi->RTCC.B.CNTEN = 0;
      RtcApip->rtcapi->RTCC.B.APIVAL = period & 0x03ffU;
      RtcApip->rtcapi->RTCC.B.CNTEN = 1;
}

/*******************************************************************************
 * @brief      enable/start the period the RTC peripheral.
 * @param[in]  pwmp      pointer to a @p RTCAPIDriver object
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void RTC_LLD_Start(const RTCAPIDriver *RtcApip)
{
    RtcApip->rtcapi->RTCC.B.CNTEN = 1;
}

/*******************************************************************************
 * @brief      disable/stop the period the RTC peripheral.
 * @param[in]  pwmp      pointer to a @p RTCAPIDriver object
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void RTC_LLD_Stop(const RTCAPIDriver *RtcApip)
{
    RtcApip->rtcapi->RTCC.B.CNTEN =  0;
}


/*******************************************************************************
 * @brief      Stop the RTC/API peripheral
 * @param[in]  rtcapip      pointer to the @p RTCAPIDriver object
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void RTCAPI_LLD_DeInit(RTCAPIDriver *RtcApip)
{
    RtcApip->rtcapi->RTCC.B.CNTEN = 0;
    /*Otherwise all other registers are accessible in supervisor mode only */
    RtcApip->rtcapi->RTCSUPV.B.SUPV = 0;

    RtcApip->rtcapi->RTCC.R = 0;
    RtcApip->rtcapi->RTCS.R = 0x20002400;

    RtcApip->rtcapi_config  = NULL;
}
